from copy import deepcopy
from collections import OrderedDict
from common.logger import log
from common.benchmark_var import ConfigOptions as ConfigOps, ConfigSections, MachineOptions as MachineOps
from framework.report.var_report import JSONFileKey as Key, ReportContentKey as ReportKey, \
    PdfContentEN as PcEN, PdfContentZH as PcZH
from common.machine_info import ExecuteCmd

SUMMARY_PARAM_LIST = [Key.MODE, Key.VERSION, Key.COPIES, Key.SCORE, Key.TESTER_NAME,
                      Key.TEST_TIME, Key.RUN_TIME, Key.REPORT_TIME]

WORKLOAD_PARAM_LIST = [Key.DESC, Key.UNIT, Key.SCORE, Key.SUB_ITEM,
                       Key.ELAPSED_TIME, Key.INDEX, Key.VALID]


class ReportKeyMapping:
    HW_DICT = {ConfigOps.PRODUCT_MODEL: [PcEN.PRODUCT_MODEL, PcZH.PRODUCT_MODEL],
               MachineOps.BIOS_VERSION: [PcEN.BIOS_VERSION, PcZH.BIOS_VERSION],
               ConfigOps.CPU_MODEL: [PcEN.CPU_MODEL, PcZH.CPU_MODEL],
               MachineOps.ARCH: [PcEN.ARCH, PcZH.ARCH],
               MachineOps.CPU_LOGIC_CORES: [PcEN.CPU_CORES, PcZH.CPU_CORES],
               MachineOps.STORAGE: [PcEN.OS_DISK, PcZH.OS_DISK], MachineOps.MEMORY: [PcEN.MEMORY, PcZH.MEMORY],
               ConfigOps.NETCARDS: [PcEN.NET_CARDS, PcZH.NET_CARDS], ConfigOps.RAID: [PcEN.RAID, PcZH.RAID],
               ConfigOps.DISPLAY_CARDS: [PcEN.DISPLAY_CARDS, PcZH.DISPLAY_CARDS],
               ConfigOps.MANUFACTURER: [PcEN.MANUFACTURER, PcZH.MANUFACTURER],
               ConfigOps.HARDWARE_OTHERS: [PcEN.HW_OTHERS, PcZH.HW_OTHERS]}

    SW_DICT = {ConfigOps.OS: [PcEN.OS, PcZH.OS], MachineOps.KERNEL: [PcEN.KERNEL, PcZH.KERNEL],
               MachineOps.COMPILER: [PcEN.COMPILER, PcZH.COMPILER],
               MachineOps.FILE_SYSTEM_PARAM: [PcEN.FILE_SYSTEM, PcZH.FILE_SYSTEM],
               MachineOps.RUN_LEVEL: [PcEN.RUN_LEVEL, PcZH.RUN_LEVEL],
               MachineOps.IO_SCHEDULER: [PcEN.IO_SCHEDULER, PcZH.IO_SCHEDULER],
               MachineOps.PAGE_SIZE: [PcEN.PAGE_SIZE, PcZH.PAGE_SIZE],
               MachineOps.HUGE_PAGE_SIZE: [PcEN.HUGE_PAGE_SIZE, PcZH.HUGE_PAGE_SIZE],
               MachineOps.BASH: [PcEN.BASH, PcZH.BASH], MachineOps.BINUTILS: [PcEN.BINUTILS, PcZH.BINUTILS],
               MachineOps.PYTHON3: [PcEN.PYTHON3, PcZH.PYTHON3], MachineOps.THP: [PcEN.THP, PcZH.THP],
               MachineOps.JAVA: [PcEN.JAVA, PcZH.JAVA], MachineOps.GLIBC: [PcEN.GLIBC, PcZH.GLIBC],
               ConfigOps.SOFTWARE_OTHERS: [PcEN.SW_OTHERS, PcZH.SW_OTHERS]}

    COMPILER_VERSION = {MachineOps.CC: ReportKey.C_VERSION, MachineOps.CXX: ReportKey.CXX_VERSION,
                        MachineOps.JAVA: ReportKey.JAVA_VERSION}

    COMPILER_OPTION = {MachineOps.C_OPTIONS: ReportKey.C_OPTIONS,
                       MachineOps.CXX_OPTIONS: ReportKey.CXX_OPTIONS,
                       MachineOps.JAVA_OPTIONS: ReportKey.JAVA_OPTIONS}

    TITLE_DICT = {ReportKey.REPORT_DATE: [PcEN.REPORT_DATE, PcZH.REPORT_DATE],
                  ReportKey.HARDWARE: [PcEN.HW_TITLE, PcZH.HW_TITLE], ReportKey.FOOTER: [PcEN.FOOTER, PcZH.FOOTER],
                  ReportKey.SOFTWARE: [PcEN.SW_TITLE, PcZH.SW_TITLE], ReportKey.HEADER: [PcEN.HEADER, PcZH.HEADER],
                  ReportKey.SUITE_SCORE: [PcEN.SUITE_SCORE, PcZH.SUITE_SCORE],
                  ReportKey.SINGLE_COPY: [PcEN.SINGLE_CORY, PcZH.SINGLE_CORY],
                  ReportKey.MULTI_COPIES: [PcEN.MULTI_COPIES, PcZH.MULTI_COPIES],
                  ReportKey.PARTIAL_RUN: [PcEN.PARTIAL_RUN, PcZH.PARTIAL_RUN],
                  ReportKey.TEST_SUITES: [PcEN.TEST_SUITES, PcZH.TEST_SUITES],
                  ReportKey.SCORE: [PcEN.SCORE, PcZH.SCORE], ReportKey.INDEX: [PcEN.INDEX, PcZH.INDEX],
                  ReportKey.SECONDS: [PcEN.SECONDS, PcZH.SECONDS], ReportKey.UNIT: [PcEN.UNIT, PcZH.UNIT],
                  ReportKey.SUITE_TOTAL_SCORE: [PcEN.SUITE_TOTAL_SCORE, PcZH.SUITE_TOTAL_SCORE],
                  ReportKey.TEST_CASES: [PcEN.TEST_CASES, PcZH.TEST_CASES],
                  ReportKey.WORKLOADS: [PcEN.WORKLOADS, PcZH.WORKLOADS],
                  ReportKey.SUB_ITEMS: [PcEN.SUB_ITEMS, PcZH.SUB_ITEMS],
                  ReportKey.SUCCESS: [PcEN.SUCCESS, PcZH.SUCCESS], ReportKey.INVALID: [PcEN.INVALID, PcZH.INVALID],
                  ReportKey.DAY: [PcEN.DAY, PcZH.DAY], ReportKey.HOUR: [PcEN.HOUR, PcZH.HOUR],
                  ReportKey.MINUTE: [PcEN.MINUTE, PcZH.MINUTE], ReportKey.SECOND: [PcEN.SECOND, PcZH.SECOND],
                  ReportKey.PARALLEL_PROCESSES: [PcEN.PARALLEL_PROCESSES, PcZH.PARALLEL_PROCESSES],
                  ReportKey.TESTCASE_STATUS: [PcEN.PDF_TESTCASE_STATUS, PcZH.PDF_TESTCASE_STATUS],
                  ReportKey.RESULT_STATUS: [PcEN.RESULT_STATUS, PcZH.RESULT_STATUS],
                  ReportKey.TESTER_NAME: [PcEN.TESTER_NAME, PcZH.TESTER_NAME],
                  ReportKey.COMPILER_VERSION: [PcEN.COMPILER_VERSION, PcZH.COMPILER_VERSION],
                  ReportKey.COMPILE_OPTION: [PcEN.COMPILE_OPTION, PcZH.COMPILE_OPTION],
                  ReportKey.ENVIORNMENT_INFO: [PcEN.ENVIORNMENT_INFO, PcZH.ENVIORNMENT_INFO],
                  ReportKey.DEFAULT: [PcEN.DEFAULT, PcZH.DEFAULT], ReportKey.DESC: [PcEN.DESC, PcZH.DESC],
                  MachineOps.JAVA: [PcEN.JAVA_VERSION, PcZH.JAVA_VERSION],
                  MachineOps.CC: [PcEN.C_VERSION, PcZH.C_VERSION],
                  MachineOps.CXX: [PcEN.CXX_VERSION, PcZH.CXX_VERSION],
                  MachineOps.C_OPTIONS: [PcEN.C_OPTIONS, PcZH.C_OPTIONS],
                  MachineOps.CXX_OPTIONS: [PcEN.CXX_OPTIONS, PcZH.CXX_OPTIONS],
                  MachineOps.JAVA_OPTIONS: [PcEN.JAVA_OPTIONS, PcZH.JAVA_OPTIONS]}

    UNKNOWN_TIPS = [PcEN.UNKNOWN, PcZH.UNKNOWN]


class PreReport:
    COMPILER_VERSION_DICT = OrderedDict({MachineOps.CC: "", MachineOps.CXX: "", MachineOps.JAVA: ""})
    COMPILE_OPTION_DICT = OrderedDict({MachineOps.C_OPTIONS: "", MachineOps.CXX_OPTIONS: "",
                                       MachineOps.JAVA_OPTIONS: ""})
    C_OPTIONS_LIST = [ConfigOps.CFLAGS, ConfigOps.CLD_FLAGS, ConfigOps.LD_FLAGS]
    CXX_OPTIONS_LIST = [ConfigOps.CXXFLAGS, ConfigOps.CXXLD_FLAGS, ConfigOps.LD_FLAGS]

    def __init__(self, json_data, options, mode_desc):
        self.system_info = json_data.get(Key.SYSTEM)
        self.bench_content = json_data.get(Key.BM_CONTENT)
        self.bm_summary = self.bench_content.get(Key.BM_SUMMARY)
        self.bm_detail = self.bench_content.get(Key.BM_DETAIL)
        self.options = deepcopy(options)
        self.mode_desc = mode_desc
        self.mode_suite = ""

    def get_report_data(self):
        try:
            sys_info = self.get_machine_info()
            bm_summary = self.get_benchmark_summary()
            bm_suite = self.get_benchmark_suite(bm_summary, self.bm_detail)
            bm_data = self.process_bm_data(self.get_benchmark_detail(self.bm_detail))
            bm_sub_item = self.get_sub_items(bm_data)
            status_num = self.get_testcase_status(bm_data)
            compile_info = self.get_compile_info()
            if None in (sys_info, bm_summary, bm_suite, bm_data, bm_sub_item, compile_info):
                log.error("Get report data failed!")
                return None
            log.info("Get report data success.")
            return ReportData(sys_info, bm_summary, bm_suite, bm_data, bm_sub_item, status_num, compile_info)
        except Exception as err:
            log.error("Get report data failed, %s" % err)
            return None

    @staticmethod
    def format_score(score):
        if score == "":
            return score
        return "%.2f" % score

    def get_machine_info(self):
        try:
            sys_info = OrderedDict({ReportKey.HARDWARE: self.system_info[Key.HARDWARE],
                                   ReportKey.SOFTWARE: self.system_info[Key.SOFTWARE]})
            return sys_info
        except KeyError as err:
            log.error("Get machine info failed, %s", err)
            return None

    def get_benchmark_summary(self):
        try:
            self.mode_suite = "{}Suite".format(self.bm_summary[Key.MODE])
            res = OrderedDict()
            for param in SUMMARY_PARAM_LIST:
                if param == Key.SCORE:
                    res[param] = self.format_score(self.bm_summary[param])
                else:
                    res[param] = self.bm_summary[param]
            return res
        except KeyError as err:
            log.error("Get benchmark summary failed, %s" % err)
            return None

    def get_benchmark_suite(self, bm_summary, bm_suite_list):
        bm_suite_res = {self.mode_suite: OrderedDict()}
        try:
            if not bm_suite_list:
                return bm_suite_res
            if bm_suite_list[0].get(Key.NAME) == self.mode_suite:
                bm_suite_list = bm_suite_list[0].get(Key.SUB_SUITES)
            for bm_suite in bm_suite_list:
                suite_name = bm_suite.get(Key.NAME)
                suite_desc = bm_suite.get(Key.DESC, "")
                suite_score = self.format_score(bm_suite.get(Key.SCORE))
                bm_suite_res[self.mode_suite][suite_name] = {Key.DESC: suite_desc, Key.SCORE: suite_score}
            bm_suite_res[self.mode_suite][Key.DESC] = self.mode_desc
            bm_suite_res[self.mode_suite][Key.SCORE] = bm_summary[Key.SCORE]
        except Exception as err:
            log.error("Get benchmark suite failed, %s" % err)
        finally:
            return bm_suite_res

    def process_bm_data(self, bm_data):
        if self.mode_suite not in bm_data:
            return {self.mode_suite: bm_data}
        else:
            return bm_data

    @staticmethod
    def get_sub_items(bm_data):
        bm_sub_item = OrderedDict()
        for sub_suite_dict in bm_data.values():
            for sub_suite_name, sub_suite_info in sub_suite_dict.items():
                workloads_dict = sub_suite_info.get(Key.WORKLOADS)
                if not workloads_dict:
                    continue
                for wd_name, wd_info in workloads_dict.items():
                    wd_full_name = "{}.{}".format(sub_suite_name, wd_name)
                    bm_sub_item[wd_full_name] = wd_info.get(Key.SUB_ITEM)
                    bm_sub_item[wd_full_name][Key.INDEX] = wd_info.get(Key.INDEX)
        return bm_sub_item

    def get_benchmark_detail(self, bm_suite_list):
        try:
            bm_suite_dict = OrderedDict()
            for suite_res in bm_suite_list:
                suite_name = suite_res.get(Key.NAME)
                if suite_name not in bm_suite_dict:
                    bm_suite_dict[suite_name] = OrderedDict()
                sub_suites = suite_res.get(Key.SUB_SUITES)
                if sub_suites:
                    self.get_sub_suites(sub_suites, bm_suite_dict[suite_name])
                else:
                    self.get_workloads(suite_res.get(Key.WORKLOADS),
                                       suite_res.get(Key.SCORE, ""), bm_suite_dict[suite_name])
            return bm_suite_dict
        except Exception as err:
            log.error("Get benchmark detail failed, %s" % err)
            return None

    def get_sub_suites(self, sub_suites, suite_dict):
        sub_suite_res = self.get_benchmark_detail(sub_suites)
        if sub_suite_res:
            for sub_suite, suite_res in sub_suite_res.items():
                suite_dict[sub_suite] = suite_res

    def get_workloads(self, workloads_info, score, workloads_dict):
        if not workloads_info:
            return
        bm_dict = OrderedDict()
        for info in workloads_info:
            name = info.get(Key.NAME)
            bm_dict[name] = OrderedDict()
            for param in WORKLOAD_PARAM_LIST:
                wd_value = info.get(param)
                if wd_value is None:
                    continue
                if param == Key.ELAPSED_TIME:
                    bm_dict[name][param] = "%.3f" % wd_value
                elif param in [Key.SCORE, Key.INDEX]:
                    bm_dict[name][param] = self.format_score(wd_value)
                elif param == Key.SUB_ITEM:
                    bm_dict[name][param] = OrderedDict()
                    self.get_workloads(wd_value, bm_dict[name][Key.SCORE], bm_dict[name][param])
                else:
                    bm_dict[name][param] = wd_value
        workloads_dict[Key.WORKLOADS] = bm_dict
        if score and type(score) is str:
            workloads_dict[Key.SCORE] = score
        else:
            workloads_dict[Key.SCORE] = self.format_score(score)

    @staticmethod
    def get_workload_status(sub_suite_info, status_num_list):
        if not sub_suite_info:
            return status_num_list
        workloads_num, success_num, failed_num = status_num_list
        for workload in sub_suite_info.get(Key.WORKLOADS).values():
            if workload.get(Key.VALID):
                success_num += 1
            else:
                failed_num += 1
            workloads_num += 1
        return [workloads_num, success_num, failed_num]

    def get_testcase_status(self, bm_data):
        bm_data = deepcopy(bm_data)
        status_num_list = [0, 0, 0]
        for suite_info in bm_data.values():
            for sub_suite_info in suite_info.values():
                if not sub_suite_info:
                    continue
                status_num_list = self.get_workload_status(sub_suite_info, status_num_list)
        return status_num_list

    def get_compile_info(self):
        try:
            del self.options[ConfigSections.GENERAL]
            compile_dict = {ReportKey.COMPILER_VERSION: OrderedDict(), ReportKey.COMPILE_OPTION: OrderedDict()}
            for compile_title, compile_config in self.options.items():
                compiler_version = deepcopy(self.COMPILER_VERSION_DICT)
                compile_options = deepcopy(self.COMPILE_OPTION_DICT)

                self.get_compiler_version(compile_title, compile_config, compiler_version)
                compile_dict[ReportKey.COMPILER_VERSION][compile_title] = compiler_version

                self.get_compile_options(compile_config, compile_options)
                compile_dict[ReportKey.COMPILE_OPTION][compile_title] = compile_options
            return compile_dict
        except Exception as err:
            log.error("Get compile info failed, %s" % err)
            return None

    @staticmethod
    def get_compiler_version(compile_title, compile_config, compiler_version):
        cc_cmd = "{} {}".format(compile_config.get(ConfigOps.CC), compile_config.get(ConfigOps.CC_VERSION))
        cxx_cmd = "{} {}".format(compile_config.get(ConfigOps.CXX), compile_config.get(ConfigOps.CXX_VERSION))
        if compile_title == ConfigSections.COMPILER:
            cmd_dict = {MachineOps.JAVA: "java -version", MachineOps.CC: cc_cmd, MachineOps.CXX: cxx_cmd}
        else:
            cmd_dict = {MachineOps.CC: cc_cmd, MachineOps.CXX: cxx_cmd}
        for cmd_key, cmd in cmd_dict.items():
            compiler_version[cmd_key] = ExecuteCmd.execute_cmd(cmd)

    def get_compile_options(self, compile_config, compile_option):
        c_options, cxx_options = "", ""
        for c_option in self.C_OPTIONS_LIST:
            c_options += compile_config.get(c_option, "")
        for cxx_option in self.CXX_OPTIONS_LIST:
            cxx_options += compile_config.get(cxx_option, "")
        compile_option[MachineOps.C_OPTIONS] = c_options
        compile_option[MachineOps.CXX_OPTIONS] = cxx_options
        compile_option[MachineOps.JAVA_OPTIONS] = compile_config.get(ConfigOps.JAVA_OPTIONS, "")


class ReportData:
    def __init__(self, sys_info, bm_summary, bm_suite, bm_data, bm_sub_item, status_num, compile_info):
        self.sys_info = sys_info
        self.bm_summary = bm_summary
        self.bm_suite = bm_suite
        self.bm_data = bm_data
        self.bm_sub_item = bm_sub_item
        self.status_num = status_num
        self.compile_info = compile_info
